package org.biloxi.commands.impl;

import org.biloxi.card.Card;
import org.biloxi.commands.Command;
import org.biloxi.commands.CommandOverride;
import org.biloxi.commands.CommandRepository;
import org.biloxi.commands.OptionsModifier;
import org.biloxi.commands.exceptions.CommandOverriddenException;
import org.biloxi.commands.movecard.MoveCard;
import org.biloxi.model.impl.GameStateImpl;
import org.biloxi.model.mutable.GameStateMutable;
import org.biloxi.model.player.Zone;

/**
 * 
 * Implementation of a Command.
 * All Biloxi Commands must be related to CommandImpl, as we validate Commands 
 * against this Class.  
 * 
 * @author Peter.WM.Wong@gmail.com
 * 
 * @see {@link GameStateImpl#do_(Command, Object)} performs the Command validation.
 * 
 * @param <O>
 * @param <T>
 */
public abstract class CommandImpl<O,T> extends EventDispatcherImpl<O,T> implements Command<O,T>{	
	
	private 		CommandOverride<O> 	_replacement = null;
	private 		Card 				_overridingCard = null;
	private 		OptionsModifier<O> 	_optionsModifier = null;
	
	protected final CommandRepository 	c;
	
	public CommandImpl(CommandRepository cr) {
		super();
		c = cr;
	}

	/**
	 * {@link super#modifyOptions(CommandOverride)}
	 */
	public final void modifyOptions(OptionsModifier<O> om){
		_optionsModifier = om;
	}

	/**
	 * {@link super#insteadDo(CommandOverride)]
	 */
	public final void insteadDo(CommandOverride<O> cmd){
		_replacement = cmd;
	}
		
	/**
	 * Executes replacement Command, if available {@link #insteadDo(CommandImpl)}, and 
	 * throw a CommandOverriddenException.
	 * 
	 * Otherwise execute default behavior (with a modified or unmodifed Options) and 
	 * notify CommandListeners with Command Results.
	 * 
	 * As you may notice there is no try/catch block for the CommandOverriddenException.
	 * This allows Commands to be "blocked" by sub Commands.
	 * For example, a Card might say: "Prevent cards from coming into play".
	 * The Card would override the {@link MoveCard} Command when cards are being moved to
	 * the InPlay {@link Zone}.  The {@link PlayCard} Command would be blocked and SHOULDN'T
	 * notify it's listeners, since it didn't actually execute and has no Result.
	 *  
	 * @param options
	 * @param gs
	 */
	public final T execute(O options, GameStateMutable gameState) throws CommandOverriddenException {
		System.out.println("Command: "+getClass().getSimpleName()+"["+(new Exception()).getStackTrace().length+"]"+"\n"+
								"\tOptions: "+ options.toString()+"\n"
		);
		
		// Execute override (if it exists) 
		if(_replacement != null){
			_replacement.do_(options, gameState);
			throw new CommandOverriddenException(this,_overridingCard);
			
		// Execute self and notify listeners of Results 
		}else{
			return notifyListeners( this, 
									(_optionsModifier!=null)
										? _optionsModifier.modify(options)
										: options, 
									executeImpl(options, gameState), 
									gameState );
		}
	}
	
	/**
	 * Manipulates the GameState based on options.
	 * Returns result data that may be interesting to CommandListeners. 
	 * 
	 * @param o
	 * @param gameState TODO
	 * @param gs
	 * @return
	 */
	protected abstract T executeImpl(O o, GameStateMutable gameState) throws CommandOverriddenException;
	
	
}
